package org.swift.commons.deployer.framework.axis2;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;

import javax.activation.DataHandler;
import javax.xml.stream.XMLStreamException;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.OMText;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultDetail;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.axis2.transport.http.ApplicationXMLFormatter;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.transport.http.util.URLTemplatingUtil;
import org.apache.axis2.util.JavaUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Returns the results without a soap envelope.  
 * 
 * This takes the soap envelop results and returns the decoded base64
 * 
 * @author Jon roberts
 *
 */
public class DirectResultFormatter implements MessageFormatter {

	 private static final Log log = LogFactory.getLog(ApplicationXMLFormatter.class);
	    public byte[] getBytes(MessageContext 
	                           messageContext, 
	                           OMOutputFormat format) throws AxisFault {
	        return getBytes(messageContext, format, false);
	    }
	    
	    /**
	     * Get the bytes for this message
	     * @param messageContext
	     * @param format
	     * @param preserve (indicates if the OM should be preserved or consumed)
	     * @return
	     * @throws AxisFault
	     */
	    public byte[] getBytes(MessageContext messageContext, 
	                           OMOutputFormat format, 
	                           boolean preserve) throws AxisFault {

	        if (log.isDebugEnabled()) {
	            log.debug("start getBytes()");
	            log.debug("  fault flow=" + 
	                      (messageContext.getFLOW() == MessageContext.OUT_FAULT_FLOW));
	        }
	        try {
	            OMElement omElement;

	            // Find the correct element to serialize.  Normally it is the first element
	            // in the body.  But if this is a fault, use the detail entry element or the 
	            // fault reason.
	            if (messageContext.getFLOW() == MessageContext.OUT_FAULT_FLOW) {
	                SOAPFault fault = messageContext.getEnvelope().getBody().getFault();
	                SOAPFaultDetail soapFaultDetail = fault.getDetail();
	                omElement = soapFaultDetail.getFirstElement();

	                if (omElement == null) {
	                    omElement = fault.getReason();
	                }

	            } else {
	                // Normal case: The xml payload is the first element in the body.
	                omElement = messageContext.getEnvelope().getBody().getFirstElement();
	            }
	            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();

	            if (omElement != null) {

	                try {
	                    if (preserve) {
	                        omElement.serialize(bytesOut, format);
	                    } else {
	                        omElement.serializeAndConsume(bytesOut, format);
	                    }
	                } catch (XMLStreamException e) {
	                    throw AxisFault.makeFault(e);
	                }

	                return bytesOut.toByteArray();
	            }

	            return new byte[0];
	        } finally {
	            if (log.isDebugEnabled()) {
	                log.debug("end getBytes()");
	            }
	        }
	    }

	    public void writeTo(MessageContext messageContext, OMOutputFormat format,
	                        OutputStream outputStream, boolean preserve) throws AxisFault {

	        if (log.isDebugEnabled()) {
	            log.debug("start writeTo()");
	        }
	        try {
	            OMElement omElement = null;

	            if (messageContext.getFLOW() == MessageContext.OUT_FAULT_FLOW) {
	                SOAPFault fault = messageContext.getEnvelope().getBody().getFault();
	                SOAPFaultDetail soapFaultDetail = fault.getDetail();
	                if (soapFaultDetail != null) {
	                    omElement = soapFaultDetail.getFirstElement();	                    
	                }
	                if (omElement == null) {
	                    omElement = fault.getReason();
	                }

	            } else {
	                omElement = messageContext.getEnvelope().getBody().getFirstElement();
	               	                	               
	            }
	            if (omElement != null
	            	&& omElement.getFirstOMChild() instanceof OMText) {
	            		OMText text = (OMText)omElement.getFirstOMChild();
	            		DataHandler handler = (DataHandler)text.getDataHandler();
            		try {	            			
						handler.writeTo(outputStream);
					} catch (IOException e) {
						 throw AxisFault.makeFault(e);
					}	     
	            } else {
	            	omElement = messageContext.getEnvelope().getBody().getFirstElement();
	            	try {
						omElement.serialize(outputStream);
					} catch (XMLStreamException e) {
						 throw AxisFault.makeFault(e);
					}
	            }
	            try {
	                outputStream.flush();
	            } catch (IOException e) {
	            	 throw AxisFault.makeFault(e);
	            }
	        } finally {
	            if (log.isDebugEnabled()) {
	                log.debug("end writeTo()");
	            }
	        }
	    }

	    public String getContentType(MessageContext messageContext, OMOutputFormat format,
	                                 String soapAction) {

	    	//hax, I don't know how to put the encoding on the format from the module
	    	String encoding;
	        String contentType;
	        if (messageContext.getFLOW() == MessageContext.OUT_FAULT_FLOW) {
	        	encoding = format.getCharSetEncoding();
	        	contentType = (String) messageContext.getProperty(Constants.Configuration.CONTENT_TYPE);
	    	} else {
	    		encoding = (String)messageContext.getProperty("swift.encoding_type");
	    		contentType = (String) messageContext.getProperty(Constants.Configuration.MESSAGE_TYPE);
	    	}

	        if (log.isDebugEnabled()) {
	            log.debug("contentType set from messageContext =" + contentType);
	            log.debug("(NOTE) contentType from format is=" + format.getContentType());
	        }
	        
	        if (contentType == null) {
	            contentType = HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED;
	        } else if (isSOAPContentType(contentType)) {
	            contentType = HTTPConstants.MEDIA_TYPE_APPLICATION_XML;
	            if (log.isDebugEnabled()) {
	                log.debug("contentType is set incorrectly for Application XML.");
	                log.debug("It is changed to " + contentType);
	            }
	        }

	        if (encoding != null) {
	            contentType += "; charset=" + encoding;
	        }
	       
	        if (log.isDebugEnabled()) {
	            log.debug("contentType returned =" + contentType);
	        }
	        return contentType;
	    }

	    public URL getTargetAddress(MessageContext messageContext, 
	                                OMOutputFormat format, 
	                                URL targetURL)
	            throws AxisFault {

	        // Check whether there is a template in the URL, if so we have to replace then with data
	        // values and create a new target URL.
	        targetURL = URLTemplatingUtil.getTemplatedURL(targetURL, messageContext, false);

	        return targetURL;
	    }

	    public String formatSOAPAction(MessageContext messageContext, OMOutputFormat format,
	                                   String soapAction) {
	        return soapAction;
	    }
	    
	    private boolean isSOAPContentType(String contentType) {
	        if (JavaUtils.indexOfIgnoreCase(contentType, SOAP12Constants.SOAP_12_CONTENT_TYPE) > -1) {
	            return true;
	        }
	        // search for "type=text/xml"
	        else if (JavaUtils.indexOfIgnoreCase(contentType, 
	                                             SOAP11Constants.SOAP_11_CONTENT_TYPE) > -1) {
	            return true;
	        }
	        return false;
	    }

	

}
